package com.ubxtech.web.utils;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson2.JSON;
import com.ubxtech.core.utils.AesUtil;
import com.ubxtech.core.utils.Sm4Util;
import com.ubxtech.web.annotation.ResponseEncrypt;
import com.ubxtech.web.properties.AuthProperties;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * @author Victor.Xiao
 * @since 2022-06-22 20:36
 **/
@Slf4j
public class ReflectMethodUtil {

    /**
     * 需加密字段
    */
    private static final ConcurrentHashMap<String, List<Field>> CLASSES_ENCRYPT_FIELD = new ConcurrentHashMap<>();

    /**
     * 第一个key是注解名字
     * value对应的ConcurrentHashMap代表每一种注解解析得到的类对应的字段,key对应的是类名,List<Field>对应的是类中含有注解的字段
    */
    private static final ConcurrentHashMap<String, ConcurrentHashMap<String, List<Field>>> CLASSES_ANNOTATION_FIELD = new ConcurrentHashMap<>();

    /**
     * 得到方法返回结果的泛型
     * @author Victor.Xiao
     * @since 2022-06-23 10:42
     * @param method method
     * @return java.util.List<java.lang.reflect.Type>
    */
    public static List<Type> getMethodGenericReturnTypes(Method method) {

        System.out.println(method.getDeclaringClass().getName()+"."+method.getName());
        List<Type> typeList = new ArrayList<>();
        //判断是否带有泛型
        Type type = method.getGenericReturnType();
        if (type instanceof ParameterizedType) {
            typeList.add(((ParameterizedType) type).getRawType());
            Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
            while (actualTypeArguments.length > 0) {
                type = actualTypeArguments[0];
                if (type instanceof ParameterizedType) {
                    typeList.add(((ParameterizedType) type).getRawType());
                    actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
                } else {
                    typeList.add(type);
                    break;
                }
            }
        } else {
            typeList.add(type);
        }
        return typeList;
    }

    /**
     * 得到方法返回结果的泛型,排除掉java自带的类,排除未定义的类,比如?或者T这种形式
     * @author Victor.Xiao
     * @since 2022-06-23 11:29
     * @param method method
     * @return java.util.List<java.lang.reflect.Type>
    */
    public static List<Type> getMethodGenericReturnTypesCustoms(Method method) {
        List<Type> types = getMethodGenericReturnTypes(method);
        if (types.isEmpty()) {
            return types;
        }
        return types.stream().filter(type -> !type.getTypeName().contains("sun.")
                && !type.getTypeName().contains("java.")
                && type.getTypeName().contains(".")
                && !type.getTypeName().contains("com.ubxtech.core.context.Result")).collect(Collectors.toList());

    }

    /**
     * 获取class中标识的字段
     * @author Victor.Xiao
     * @since 2022-06-23 14:00
     * @param type type
     * @return java.util.List<java.lang.reflect.Field>
    */
    public static List<Field> getEncryptField(Type type) {
        List<Field> fieldList = CLASSES_ENCRYPT_FIELD.get(type.getTypeName());
        if (CLASSES_ENCRYPT_FIELD.get(type.getTypeName()) != null) {
            return fieldList;
        }
        fieldList = new ArrayList<>();
        try {
            Class classes = Class.forName(type.getTypeName());
            Field[] fields = classes.getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(ResponseEncrypt.class)) {
                    field.setAccessible(true);
                    fieldList.add(field);
                }
            }
            CLASSES_ENCRYPT_FIELD.put(type.getTypeName(),fieldList);
        } catch (ClassNotFoundException e) {
            log.error("获取class中标识的字段有误",e);
        }
        return fieldList;
    }


    /**
     * 获取class中标识的字段
     * @author Victor.Xiao
     * @since 2022-06-23 14:00
     * @param obj obj
     * @return java.util.List<java.lang.reflect.Field>
     */
    public static List<Field> getAnnotationField(Object obj,Class<? extends Annotation> clazz) {
        if (obj == null) {
            return new ArrayList<>();
        }
        String className = obj.getClass().getTypeName();
        if (className.contains("sun.") || className.contains("java.")) {
            return new ArrayList<>();
        }
        String annotationName = clazz.getTypeName();
        ConcurrentHashMap<String, List<Field>> classFieldMap = CLASSES_ANNOTATION_FIELD.computeIfAbsent(annotationName, key -> new ConcurrentHashMap<>());
//        List<Field> fieldList = classFieldMap.computeIfAbsent(className, key -> new ArrayList<>());
        //可能解析过,就是没这个注解的字段,也返回
        List<Field> fieldList = classFieldMap.get(className);
        if (fieldList != null) {
            return fieldList;
        }
        //为空就是还没解析过
        fieldList = new ArrayList<>();
        classFieldMap.put(className, fieldList);
        try {
            //获得所有字段
            Field[] fields = obj.getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(clazz)) {
                    fieldList.add(field);
                }
            }
        } catch (Exception e) {
            log.error("获取class中注解字段有误,{}", clazz, e);
        }
        return fieldList;
    }

    public static void main(String[] args) throws IllegalAccessException {
        AuthProperties authProperties = new AuthProperties();
        authProperties.setType("test");
        ReflectMethodUtil reflectMethodUtil = new ReflectMethodUtil();
        for (Method method : reflectMethodUtil.getClass().getMethods()) {
            if (method.getName().contains("test")) {
                for (Type methodReturnType : ReflectMethodUtil.getMethodGenericReturnTypesCustoms(method)) {
                    System.out.println(method.getName()+"-------"+methodReturnType.getTypeName());
                    List<Field> fields = ReflectMethodUtil.getEncryptField(methodReturnType);
                    for (Field field : fields) {
                        field.set(authProperties,"test=====");
                    }
                }
            }
        }
        System.out.println(authProperties);
        String fieldStr = "123";
        System.out.println(JSON.toJSONString(fieldStr));
        int fieldInt = 123;
        System.out.println(JSON.toJSONString(fieldInt));

        //FE47BA1588B7F5F8
        System.out.println(Sm4Util.encryptHex("123","FE47BA1588B7F5F8"));
        System.out.println(Sm4Util.decryptStr("2ed92c851ac8706e1d05b0da530c2b57","aaaaaaaaaaaaaaaa"));

        System.out.println(AesUtil.decode("ze4r0kJTGhiBJgOxv/94Vj7IXvy3h2lPNSGHS5BOsTs=","aaaaaaaaaaaaaaaa"));
    }
}
